home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 26 / Cream of the Crop 26.iso / program / p063b9s.zip / UNIT / MAILSCAN.PAS < prev    next >
Pascal/Delphi Source File  |  1997-03-02  |  59KB  |  1,874 lines

  1. UNIT MailScan;
  2. {╔══════════════════════════════════════════════════════════════════════════╗}
  3. {║ Mail processor                                Last changed: 02.03.97  SA ║}
  4. {║                                                                          ║}
  5. {║                         (C) Copyright 1989-97 by                         ║}
  6. {║       Dan Wulff, Jens Sandalgaard, Steen Christensen & S¢ren Ager        ║}
  7. {║                                                                          ║}
  8. {║ This source may not be given to anybody, without the written permission  ║}
  9. {║ from The Portal Team.                                                    ║}
  10. {╚══════════════════════════════════════════════════════════════════════════╝}
  11. {$I POPDEFS.INC}
  12.  
  13. INTERFACE
  14.  
  15. USES Use32, Dos, OpDate, OpString, OpDos, OpCrt, OpCmd, OpEntry, OpWindow, OpMenu,
  16.      OpFrame, OpField,
  17.      Input, Globals, OproUtil, StrUtil, MailUtil, Crc, NetFile, Send2Utl,
  18.      LogFile, NodeList, FileUtil, PopTypes, MailPack, PFix, Usage, MSDefs;
  19.  
  20. TYPE
  21.   SendPktPtr=^SendPktType;
  22.   SendPktType=RECORD
  23.     PktFile    : FILE;
  24.     PktBufPos,
  25.     PktBufSiz  : WORD;
  26.     PktBuf     : Pointer;
  27.     PktAdr     : TFidoAddress; { Address of receiver }
  28.     Next       : SendPktPtr;
  29.   END;
  30.  
  31. PROCEDURE RunMailScanner(Flags:LongInt);
  32.  
  33. IMPLEMENTATION
  34.  
  35. USES OpSelect, OpRoot,
  36.      Util, ArcView, OpusMsg, Resource, MSMisc;
  37.  
  38. PROCEDURE FlushPkt(VAR Sp:SendPktPtr);
  39. BEGIN
  40.   WITH Sp^ DO
  41.   BEGIN
  42.     BLOCKWRITE(PktFile,PktBuf^,PktBufPos);
  43.     PktBufPos:=0;
  44.   END;
  45. END;
  46.  
  47. PROCEDURE AddToPktBuffer(VAR Sp:SendPktPtr; VAR BufAdr; Num:WORD);
  48. VAR
  49.   BufOfs:WORD;
  50.   x:LongInt;
  51. BEGIN
  52.   WITH Sp^ DO
  53.   BEGIN
  54.     IF PktBufSiz=0 THEN BlockWrite(PktFile,BufAdr,Num) ELSE
  55.     BEGIN
  56.       BufOfs:=0;
  57.       REPEAT
  58.         x:=PktBufSiz-PktBufPos;
  59.         IF x>Num THEN x:=Num;
  60.         MoveFast(Ct(BufAdr)[BufOfs+1],CT(PktBuf^)[PktBufPos+1],x);
  61.         INC(PktBufPos,x);
  62.         IF PktBufPos=PktBufSiz THEN FlushPkt(Sp);
  63.         DEC(Num,WORD(x));
  64.         INC(BufOfs,WORD(x));
  65.       UNTIL Num=0;
  66.     END;
  67.   END;
  68. END;
  69.  
  70. PROCEDURE RunMailScanner(Flags:LongInt);
  71. VAR
  72.   GemRp                : RunParametersType;
  73.   QBase                : ^QBBSBaseType;
  74.   OpusBase             : ^OpusBaseType;
  75.  
  76.   Pmh                  : TPktMsgHeader;
  77.   PMHByte              : ARRAY[1..SizeOf(TPktMsgHeader)] OF BYTE ABSOLUTE Pmh;
  78.   Dupe                 : DupeTabType;
  79.   Buffer               : Pointer;
  80.   MailScanWin          : WindowPtr;
  81.   IsInSeenBy           : ARRAY[1..2,1..50] OF Boolean;
  82.   PktBufCount,
  83.   ScannerAreaNum,
  84.   NumPath, NumSeenBy,
  85.   SeenByOffSet, OriginOffSet,
  86.   PathOffSet, InBuffer,
  87.   BufPos,BadNum,TagEndPos,
  88.   MsgSlut,RealMsgLen,
  89.   MaxPktBuffer         : WORD;
  90.   Cmd, DupeMsgNum,
  91.   BadMsgNum, MatrixNum : INTEGER;
  92.   SeenByTab            : ^SeenByTabType;
  93.   PathTab              : ^PathTabType;
  94.   PktEnd,
  95.   PktError             : Boolean;
  96.   PktFile, DupeFile    : TNetFile;
  97.   TmpAreas, AreasBBS   : AreasBBSPtr;
  98.   Msg                  : MessageTypePtr;
  99.   SendPkt              : SendPktPtr;
  100.   ESR                  : TPoPEntryScreen;
  101.   MsgScannedCount,
  102.   MsgSentCount,
  103.   MsgTossedCount,
  104.   MsgBadCount,
  105.   MsgDupeCount         : LongInt;
  106.   BasePath             : PathStr;
  107.   rp                   : Pointer;
  108.   CurrentAKA,i         : BYTE;
  109.   PktOrig,PktDest      : TFidoAddress;
  110.   StartTime,EndTime,
  111.   MSTimer              : DateTimeRec;
  112.   OldTagName           : S40;
  113.  
  114.   PROCEDURE GetDT(VAR DT:DateTimeRec);
  115.   BEGIN
  116.     WITH Dt DO
  117.     BEGIN
  118.       T:=CurrentTime;
  119.       D:=Today;
  120.     END;
  121.   END;
  122.  
  123.   FUNCTION StopMSTimer:LongInt;
  124.   VAR
  125.     Now:DateTimeRec;
  126.     Days:WORD;
  127.     Secs:LongInt;
  128.   BEGIN
  129.     GetDT(Now);
  130.     DateTimeDiff(Now,MSTimer,Days,Secs);
  131.     IF (Days=0) AND (Secs=0) THEN Secs:=1;
  132.     StopMSTimer:=Secs+LongInt(LongInt(Days)*SecondsInDay);
  133.   END;
  134.  
  135.   PROCEDURE OpusMsgToMSMsg(VAR h:MsgHdrType; p:Pointer; Len:WORD; VAR Msg:MessageTypePtr);
  136.   BEGIN
  137.     FillChar(Msg^,SizeOf(Msg^),0);
  138.     WITH Msg^ DO
  139.     BEGIN
  140.       MoveFast(p^,TextBody,Len);
  141.       MoveFast(h.datetime,pmh.time,20);
  142.       WhoTo:=AsciiZ2Str(h.ToUser,36);
  143.       WhoFrom:=AsciiZ2Str(h.FromUser,36);
  144.       Subject:=AsciiZ2Str(h.Subject,72);
  145.       OrigNet:=h.OrigNet;
  146.       OrigNode:=h.OrigNode;
  147.       DestNet:=h.DestNet;
  148.       DestNode:=h.DestNode;
  149.       MsgLen:=Len;
  150.     END;
  151.   END;
  152.  
  153.   FUNCTION SecurityOK:Boolean;
  154.   VAR
  155.     i:BYTE;
  156.   BEGIN
  157.     SecurityOK:=True;
  158.     IF NOT Cfg.MailScanner.Secure THEN Exit;
  159.     WITH TmpAreas^.Area DO
  160.     BEGIN
  161.       FOR i:=1 TO TmpAreas^.SendNum[1] DO
  162.         WITH SendToTabType(TmpAreas^.SendTo[1]^)[i] DO
  163.           IF (Node=Msg^.OrigNode) AND (Net=Msg^.OrigNet) THEN Exit;
  164.     END;
  165.     SecurityOK:=False;
  166.   END;
  167.  
  168.   PROCEDURE Message(CONST s: S60; YPos: BYTE);
  169.   BEGIN
  170.     MailScanWin^.wFastWrite(CPad(s,60),YPos,16,Cfg.Color[2].HighLightColor);
  171.   END;
  172.  
  173.   PROCEDURE UsedMemStatus;
  174.   BEGIN
  175.     Message('Available='+LongIntForm('########',MaxAvail)+', # of buffers='+Long2Str(PktBufCount),18);
  176.   END;
  177.  
  178.   PROCEDURE DoMailScan;
  179.   VAR
  180.     i,j       : Byte;
  181.     DelSr, sr : SearchRec;
  182.     NodeStat  : TNodeStat;
  183.  
  184.     FUNCTION IsADupeMsg : Boolean;
  185.     LABEL
  186.       NoMore,GetIt;
  187.     VAR
  188.       i:BYTE;
  189.       x:WORD;
  190.       Found,b:Boolean;
  191.       fp,n:LongInt;
  192.       Td:DupeType;
  193.       p:DupeMemTypePtr;
  194.     BEGIN
  195.       FillChar(td,SizeOf(Td),0);
  196.       WITH Td DO
  197.       BEGIN
  198.         FOR i:=1 TO Length(Msg^.whoto) DO
  199.           WhoToCRC:=UpdCrc16(BYTE(Msg^.WhoTo[i]),WhoToCRC);
  200.         FOR i:=1 TO Length(Msg^.WhoFrom) DO
  201.           WhoFromCRC:=UpdCrc16(BYTE(Msg^.WhoFrom[i]),WhoFromCRC);
  202.         FOR i:=1 TO Length(Msg^.Subject) DO
  203.           SubjectCRC:=UpdCrc16(BYTE(Msg^.Subject[i]),SubjectCRC);
  204.         FOR i:=15 TO 34 DO
  205.           DateCRC:=UpdCrc16(PMHByte[i],DateCRC);
  206.       END;
  207.       i:=0;
  208.       REPEAT
  209.         INC(i);
  210.         Found:=(Dupe[i]^.d.Tag=Msg^.Tag);
  211.       UNTIL (i=MaxDupeBases) OR (Found) OR (Dupe[i]^.d.Tag='');
  212.       IF NOT Found THEN
  213.       BEGIN
  214.         IF Dupe[i]^.d.Tag='' THEN
  215.         BEGIN
  216. GetIt:
  217.           WITH Dupe[i]^ DO
  218.           BEGIN
  219.             DFPos:=TmpAreas^.DupePos;
  220.             IF DFPos=-1 THEN
  221.             BEGIN
  222.               DFPos:=DupeFile.FILESIZE;
  223.               TmpAreas^.DupePos:=DFPos;
  224.               FillChar(D,SIZEOF(DupeBaseType),0);
  225.               d.Tag:=Msg^.Tag;
  226.               DupeFile.PutRec(D,DFPos);
  227.               DupeFile.Lock(DFPos,Wait);
  228.             END ELSE
  229.               DupeFile.GetRec(D,DFPos,Keep,Wait);
  230.           END;
  231.         END
  232.         ELSE
  233.         BEGIN
  234.           WITH Dupe[MaxDupeBases]^ DO
  235.           BEGIN
  236.             DupeFile.PutRec(d,DFPos);
  237.             i:=MaxDupeBases;
  238.             GOTO GetIt;
  239.           END;
  240.         END;
  241.       END
  242.       ELSE
  243.       BEGIN
  244.         IF i>1 THEN
  245.         BEGIN
  246.           p:=Dupe[i];
  247.           MoveFast(Dupe[1],Dupe[2],(i-1)*SizeOf(Pointer));
  248.           Dupe[1]:=p;
  249.         END;
  250.       END;
  251.       WITH Dupe[1]^.d DO
  252.         IF OldTagName<>Tag THEN
  253.         BEGIN
  254.           Message(Tag,6);
  255.           OldTagName:=Tag;
  256.         END;
  257.       b:=False;
  258.       FOR x:=1 TO Dupe[1]^.d.SigNum DO
  259.         WITH Dupe[1]^.d.Sig[x] DO
  260.         BEGIN
  261.           IF (DateCRC=Td.DateCRC) AND
  262.              (WhoFromCRC=Td.WhoFromCRC) AND
  263.              (WhoToCRC=Td.WhoToCRC) AND
  264.              (SubjectCRC=Td.SubjectCRC) THEN
  265.           BEGIN
  266.             b:=True;
  267.             GOTO NoMore;
  268.           END;
  269.         END;
  270.       WITH Dupe[1]^.d DO
  271.       BEGIN
  272.         IF SigNum=1000 THEN
  273.         BEGIN
  274.           MoveFast(Sig[2],Sig[1],999*SizeOf(DupeType));
  275.         END ELSE
  276.           INC(SigNum);
  277.         Sig[SigNum]:=Td;
  278.       END;
  279. NoMore:
  280.       IsADupeMsg:=b;
  281.     END;
  282.  
  283.     PROCEDURE SetUpPathAndSeenBy;
  284.     VAR
  285.       j,i:WORD;
  286.  
  287.       FUNCTION AddToSeenBy(CONST A: TFidoAddress):Boolean;
  288.       VAR
  289.         b:Boolean;
  290.         i,CurNet,LastNetPos:INTEGER;
  291.       BEGIN
  292.         b:=True;
  293.         CurNet:=SeenByTab^[2];
  294.         LastNetPos:=1;
  295.         i:=2;
  296.         WHILE (i<NumSeenBy) AND (CurNet<A.Net) DO
  297.         BEGIN
  298.           INC(i);
  299.           IF SeenByTab^[i]=-1 THEN
  300.           BEGIN
  301.             LastNetPos:=i;
  302.             CurNet:=SeenByTab^[i+1];
  303.             INC(i,2);
  304.           END;
  305.         END;
  306.         IF CurNet<>A.Net THEN
  307.         BEGIN
  308.           IF CurNet<A.Net THEN
  309.           BEGIN
  310.             SeenByTab^[NumSeenBy+1]:=-1;
  311.             SeenByTab^[NumSeenBy+2]:=A.Net;
  312.             SeenByTab^[NumSeenBy+3]:=A.Node;
  313.             LastNetPos:=NumSeenBy+1;
  314.             INC(NumSeenBy,3);
  315.           END ELSE
  316.           BEGIN
  317.             MoveFast(SeenByTab^[LastNetPos],SeenByTab^[LastNetPos+3],2*(NumSeenBy-LastNetPos+1));
  318.             SeenByTab^[LastNetPos]:=-1;
  319.             SeenByTab^[LastNetPos+1]:=A.Net;
  320.             SeenByTab^[LastNetPos+2]:=A.Node;
  321.             INC(NumSeenBy,3);
  322.           END;
  323.         END ELSE
  324.         BEGIN
  325.           i:=LastNetPos+2;
  326.           WHILE (i<NumSeenBy) AND (SeenByTab^[i]<>-1) AND (SeenByTab^[i]<A.Node) DO
  327.             INC(i);
  328.           IF (SeenByTab^[i]=A.Node) THEN
  329.             b:=False
  330.           ELSE
  331.           BEGIN
  332.             IF i<NumSeenBy THEN
  333.             BEGIN
  334.               MoveFast(SeenByTab^[i],SeenByTab^[i+1],2*(NumSeenBy-i+1));
  335.               SeenByTab^[i]:=A.Node;
  336.               INC(NumSeenBy);
  337.             END ELSE
  338.             BEGIN
  339.               INC(NumSeenBy);
  340.               SeenByTab^[NumSeenBy]:=A.Node;
  341.             END;
  342.           END;
  343.         END;
  344.         AddToSeenBy:=b;
  345.       END;
  346.  
  347.       PROCEDURE CleanSeenByAndPath;
  348.  
  349.         PROCEDURE CleanIt(VAR TabAdr; VAR Num:WORD);
  350.         VAR
  351.           First,Last,i : WORD;
  352.           Found : Boolean;
  353.         BEGIN
  354.           i:=0;
  355.           Found:=False;
  356.           REPEAT
  357.             INC(i);
  358.             IF (SeenByTabType(TabAdr)[i]=-1) AND
  359.                (SeenByTabType(TabAdr)[i+1]=Cfg.PointNet) THEN Found:=True;
  360.           UNTIL Found OR (i>=Num);
  361.           IF Found THEN
  362.           BEGIN
  363.             First:=i;
  364.             Last:=0;
  365.             REPEAT
  366.               INC(i);
  367.               IF SeenByTabType(TabAdr)[i]=-1 THEN Last:=i;
  368.             UNTIL (Last>0) OR (i>Num);
  369.             IF Last=0 THEN { Last of nets }
  370.             BEGIN
  371.               Num:=First-1;
  372.             END ELSE
  373.             BEGIN { In the middle of nowhere }
  374.               MoveFast(SeenByTabType(TabAdr)[Last],SeenByTabType(TabAdr)[First],(Num-Last+1)*2);
  375.               INC(Num,(First-Last));
  376.             END;
  377.           END;
  378.         END;
  379.  
  380.       BEGIN
  381.         CleanIt(SeenByTab^,NumSeenBy);
  382.         CleanIt(PathTab^,NumPath);
  383.       END;
  384.  
  385.     BEGIN
  386.       { Insert receiving nodes in seenby }
  387.       FOR j:=1 TO 2 DO
  388.         FOR i:=1 TO TmpAreas^.SendNum[j] DO
  389.           WITH SendToTabType(TmpAreas^.SendTo[j]^)[i] DO
  390.           BEGIN
  391.             IF (Net<>Cfg.PointNet) AND (Point=0) THEN
  392.               IsInSeenBy[j,i]:=NOT AddToSeenBy(SendToTabType(TmpAreas^.SendTo[j]^)[i])
  393.             ELSE
  394.             BEGIN
  395.               IsInSeenBy[j,i]:=CmpAdr(SendToTabType(TmpAreas^.SendTo[j]^)[i],PktOrig);
  396.             END;
  397.           END;
  398.       IF Cfg.MailScanner.SetAKASent THEN
  399.       BEGIN
  400.         j:=TmpAreas^.Area.UsedAka;
  401.         IF j=0 THEN j:=Cfg.MainAdrNum;
  402.         FOR i:=1 TO MaxAddresses DO
  403.           IF Cfg.Addresses[i].Zone=Cfg.Addresses[j].Zone THEN
  404.             AddToSeenBy(Cfg.Addresses[i]);
  405.       END;
  406.       { Append our own number in path }
  407.       IF CFg.Addresses[CurrentAKA].Point=0 THEN
  408.       BEGIN
  409.         i:=NumPath;
  410.         IF i>0 THEN
  411.         BEGIN
  412.           WHILE (PathTab^[i]<>-1) DO
  413.             DEC(i);
  414.         END ELSE
  415.         BEGIN
  416.           i:=1;
  417.           PathTab^[1]:=-1;
  418.           PathTab^[2]:=Cfg.Addresses[CurrentAKA].Net;
  419.         END;
  420.         IF PathTab^[i+1]<>Cfg.Addresses[CurrentAKA].Net THEN
  421.         BEGIN
  422.           INC(NumPath,2);
  423.           PathTab^[NumPath-1]:=-1;
  424.           PathTab^[NumPath]:=Cfg.Addresses[CurrentAKA].Net;
  425.         END;
  426.         INC(NumPath);
  427.         PathTab^[NumPath]:=Cfg.Addresses[CurrentAKA].Node;
  428.         CleanSeenByAndPath;
  429.       END;
  430.     END;
  431.  
  432.     PROCEDURE InitPathAndSeenBy;
  433.     BEGIN
  434.       SeenByOffSet:=Msg^.MsgLen;
  435.       NumSeenBy:=3;
  436.       SeenByTab^[1]:=-1;
  437.       IF Cfg.Addresses[CurrentAKA].Point=0 THEN
  438.       BEGIN
  439.         SeenByTab^[2]:=cfg.Addresses[CurrentAKA].net;
  440.         SeenByTab^[3]:=cfg.Addresses[CurrentAKA].node;
  441.       END ELSE
  442.       BEGIN
  443.         SeenByTab^[2]:=Cfg.PointNet;
  444.         SeenByTab^[3]:=Cfg.Addresses[CurrentAKA].Point;
  445.       END;
  446.       NumPath:=2;
  447.       PathTab^[1]:=-1;
  448.       PathTab^[2]:=Cfg.Addresses[CurrentAKA].Net;
  449.       SetupPathAndSeenBy;
  450.     END;
  451.  
  452.     PROCEDURE PrepareMessageBuffer(HideSB:Boolean);
  453.     VAR
  454.       kw:S10;
  455.  
  456.       PROCEDURE AddBuffer(VAR TabAdr; VAR BufPos:WORD; Num:WORD; CONST Header:S10);
  457.       VAR
  458.         Tab:ARRAY[1..10000] OF INTEGER ABSOLUTE TabAdr;
  459.         CurNet:INTEGER;
  460.         n:WORD;
  461.         s:STRING;
  462.       BEGIN
  463.         n:=2;
  464.         CurNet:=Tab[2];
  465.         REPEAT
  466.           s:=Header;
  467.           REPEAT
  468.             INC(n);
  469.             IF (LENGTH(s)=LENGTH(Header)) THEN
  470.             BEGIN
  471.               IF Tab[n]=-1 THEN
  472.               BEGIN
  473.                 INC(n,2);
  474.                 CurNet:=Tab[n-1];
  475.               END;
  476.               s:=s+' '+Long2Str(CurNet)+'/'+Long2Str(Tab[n]);
  477.             END ELSE
  478.             BEGIN
  479.               IF Tab[n]=-1 THEN
  480.               BEGIN
  481.                 INC(n,2);
  482.                 CurNet:=Tab[n-1];
  483.                 s:=s+' '+Long2Str(CurNet)+'/'+Long2Str(Tab[n]);
  484.               END ELSE
  485.               BEGIN
  486.                 s:=s+' '+Long2Str(Tab[n]);
  487.               END;
  488.             END;
  489.           UNTIL (LENGTH(s)>76) OR (n>=Num);
  490.           s:=s+#13;
  491.           MoveFast(s[1],Msg^.TextBody[BufPos],LENGTH(s));
  492.           INC(BufPos,LENGTH(s));
  493.         UNTIL (n>=Num);
  494.       END;
  495.  
  496.     BEGIN
  497.       IF HideSB THEN kw:=#1'SEEN-BY:' ELSE kw:='SEEN-BY:';
  498.       AddBuffer(SeenByTab^,SeenByOffSet,NumSeenBy,kw);
  499.       AddBuffer(PathTab^,SeenByOffSet,NumPath,#1'PATH:');
  500.       Msg^.TextBody[SeenByOffSet]:=#0;
  501.     END;
  502.  
  503.     PROCEDURE TossIntoArea(VAR Dir:PathStr; VAR Num:INTEGER; Offset:WORD);
  504.     VAR
  505.       o:MsgHdrType;
  506.     BEGIN
  507.       IF Dir<>'' THEN
  508.       BEGIN
  509.         IF Msg^.TextBody[Msg^.MsgLen]<>#0 THEN
  510.         BEGIN
  511.           INC(Msg^.MsgLen);
  512.           Msg^.TextBody[Msg^.MsgLen]:=#0;
  513.         END;
  514.         FillChar(o,SizeOf(o),0);
  515.         WITH o DO
  516.         BEGIN
  517.           MoveFast(Msg^.WhoFrom[1],FromUser,LENGTH(Msg^.WhoFrom));
  518.           MoveFast(Msg^.WhoTo[1],ToUser,LENGTH(Msg^.WhoTo));
  519.           MoveFast(Msg^.Subject[1],Subject,LENGTH(Msg^.Subject));
  520.           MoveFast(pmh.Time,DateTime,20);
  521.           DestNode:=pmh.DestNode;
  522.           OrigNode:=pmh.OrigNode;
  523.           Cost:=pmh.Cost;
  524.           OrigNet:=pmh.OrigNet;
  525.           DestNet:=pmh.DestNet;
  526.           Attribute:=pmh.attr;
  527.         END;
  528.         INC(Num);
  529. {$IFDEF OS2}
  530.         WriteMsg(Dir,Num,o,Msg^.MsgLen,Ptr({Seg(Msg^.TextBody[OffSet]),}OFS(Msg^.TextBody[OffSet])));
  531. {$ELSE}
  532.         WriteMsg(Dir,Num,o,Msg^.MsgLen,PTR(Seg(Msg^.TextBody[OffSet]),OFS(Msg^.TextBody[OffSet])));
  533. {$ENDIF}
  534.       END;
  535.     END;
  536.  
  537.     PROCEDURE WriteHudsonMessage(VAR BufAdr; MsgStart,MsgSlut:WORD; BNum:BYTE);
  538.     VAR
  539.       Num,i:WORD;
  540.     BEGIN
  541.       QBase^.MsgHdr.Board:=BNum;
  542.       INC(QBase^.MsgInfo.TotalActive);
  543.       INC(QBase^.MsgInfo.ActiveMsgs[QBase^.MsgHdr.Board]);
  544.       INC(QBase^.MsgInfo.HighMsg);
  545.       QBase^.MsgHdr.MsgNum:=QBase^.MsgInfo.HighMsg;
  546.       WITH QBase^.MsgIdx DO
  547.       BEGIN
  548.         Board:=QBase^.MsgHdr.Board;
  549.         MsgNum:=QBase^.MsgHdr.MsgNum;
  550.       END;
  551.       QBase^.MsgIdxFile.PutRec(QBase^.MsgIdx,QBase^.MsgIdxFile.FILESIZE);
  552.       WITH QBase^ DO
  553.       BEGIN
  554.         IF MsgHdr.Msgattr AND qbReceived<>0 THEN MsgToIdx:='* Received *'
  555.                                             ELSE MsgToIdx:=MsgHdr.WhoTo;
  556.         MsgToIdxFile.PutRec(MsgToIdx,MsgToIdxFile.FILESIZE);
  557.         MsgHdr.StartRec:=QBase^.MsgTxtFile.FileSize;
  558.       END;
  559.       WITH QBase^ DO
  560.       BEGIN
  561.         MsgTxtNum:=0;
  562.         i:=MsgStart+1;
  563.         REPEAT
  564.           REPEAT
  565.             INC(MsgTxtNum);
  566.             IF i+255<=MsgSlut THEN Num:=255 ELSE Num:=MsgSlut-i+1;
  567.             MsgTxtTab[MsgTxtNum][0]:=CHAR(Num);
  568.             MoveFast(CT(BufAdr)[i],MsgTxtTab[MsgTxtNum][1],Num);
  569.             INC(i,Num);
  570.             INC(MsgHdr.NumRecs);
  571.           UNTIL (i>=MsgSlut) OR (MsgTxtNum>=QBBSMsgTxtMax);
  572.           MsgTxtFile.SEEK(MsgTxtFile.FILESIZE);
  573.           MsgTxtFile.BLOCKWRITE(MsgTxtTab[1],MsgTxtNum);
  574.           MsgTxtNum:=0;
  575.         UNTIL i>=MsgSlut;
  576.         MsgHdrFile.PutRec(MsgHdr,MsgHdrFile.FILESIZE);
  577.       END;
  578.     END;
  579.  
  580.     PROCEDURE SetAKA;
  581.     BEGIN
  582.       IF TmpAreas^.Area.UsedAKA=0 THEN CurrentAKA:=Cfg.MainAdrNum
  583.                                   ELSE CurrentAKA:=TmpAreas^.Area.UsedAKA;
  584.     END;
  585.  
  586.     PROCEDURE WriteMessage(TossIt:Boolean);
  587.     LABEL
  588.       StrangeNulPkt;
  589.     VAR
  590.       Test:INTEGER;
  591.       s:STRING;
  592.       TmpSP:SendPktPtr;
  593.       ph:TPktHeader;
  594.       i,j,n,dofw,sec100:WORD;
  595.  
  596.       PROCEDURE TossMessage; { Toss message in appropriate format }
  597.  
  598.         PROCEDURE TossIntoQuickBBS;
  599.         BEGIN
  600.           FillChar(QBase^.MsgHdr,SizeOf(QBase^.MsgHdr),0);
  601.           WITH QBase^.MsgHdr DO
  602.           BEGIN
  603.             Cost:=pmh.Cost;
  604.             IF pmh.Attr AND MsgPrivate<>0 THEN MsgAttr:=MsgAttr OR qbPrivate;
  605.             IF pmh.Attr AND MsgRead   <>0 THEN MsgAttr:=MsgAttr OR qbReceived;
  606.             PostTime[0]:=#5;
  607.             MoveFast(pmh.Time[12],PostTime[1],5);
  608.             s[0]:=#9;
  609.             MoveFast(pmh.Time[1],s[1],9);
  610.             PostDate:=DateToDateString('mm-dd-yy',DateStringToDate('dd nnn yy',s));
  611.             DestZone:=Cfg.Addresses[CurrentAKA].Zone;
  612.             OrigZone:=Cfg.Addresses[CurrentAKA].Zone;
  613.             DestNet:=pmh.DestNet;
  614.             DestNode:=pmh.DestNode;
  615.             OrigNet:=pmh.OrigNet;
  616.             OrigNode:=pmh.OrigNode;
  617.             WhoTo:=Msg^.WhoTo;
  618.             WhoFrom:=Msg^.WhoFrom;
  619.             Subj:=Msg^.Subject;
  620.           END;
  621.           WITH QBase^ DO
  622.           BEGIN
  623.             IF Msg^.Tag<>'' THEN i:=TagEndPos ELSE i:=0;
  624.             IF TmpAreas^.Area.ImportSB THEN MsgSlut:=SeenByOffSet ELSE MsgSlut:=RealMsgLen-1;
  625.             WriteHudsonMessage(Msg^.TextBody,i,MsgSlut,TmpAreas^.QNum);
  626.           END;
  627.         END;
  628.  
  629.         PROCEDURE TossIntoMsg;
  630.         BEGIN
  631.           IF OpusBase^[ScannerAreaNum]=-1 THEN
  632.           BEGIN
  633.             OpusBase^[ScannerAreaNum]:=GetHighestMsg(TmpAreas^.Area.Directory^);
  634.             IF OpusBase^[ScannerAreaNum]=0 THEN OpusBase^[ScannerAreaNum]:=1;
  635.           END;
  636.           TossIntoArea(TmpAreas^.Area.Directory^,OpusBase^[ScannerAreaNum],7+LENGTH(Msg^.Tag));
  637.         END;
  638.  
  639.       BEGIN
  640.         IF TmpAreas^.Area.ImportSB THEN
  641.         BEGIN
  642.           SeenByOffset:=RealMsgLen;
  643.           PrepareMessageBuffer(True);
  644.         END;
  645.         TmpAreas^.NewMail:=True;
  646.         IF TmpAreas^.Area.Directory^<>'' THEN
  647.         BEGIN
  648.           CASE Cfg.BBS.BBSType OF
  649.             btQBBS,btRA,btSBBS        : TossIntoQuickBBS;
  650.             btOpus110,btOpus170,btMax : TossIntoMsg;
  651.           END;
  652.           INC(MsgTossedCount);
  653.           Message(LongIntForm('########',MsgTossedCount),12);
  654.         END;
  655.       END;
  656.  
  657.     BEGIN
  658.       SetAKA;
  659.       WITH pmh DO
  660.       BEGIN
  661.         orignet:=Cfg.Addresses[CurrentAKA].Net;
  662.         orignode:=Cfg.Addresses[CurrentAKA].Node;
  663.       END;
  664.       FOR j:=1 TO 2 DO
  665.         FOR i:=1 TO TmpAreas^.SendNum[j] DO
  666.         BEGIN
  667.           IF NOT IsInSeenBy[j,i] THEN
  668.           BEGIN
  669.             TmpSP:=SendPkt;
  670.             WHILE (TmpSP<>NIL) AND (NOT CmpAdr(SendToTabType(TmpAreas^.SendTo[j]^)[i],TmpSP^.PktAdr)) DO
  671.               TmpSP:=TmpSP^.Next;
  672.             IF TmpSP=NIL THEN { Open new file }
  673.             BEGIN
  674.               New(TmpSP);
  675.               TmpSP^.Next:=SendPkt;
  676.               SendPkt:=TmpSP;
  677.               TmpSP^.PktAdr:=SendToTabType(TmpAreas^.SendTo[j]^)[i];
  678.               Assign(TmpSP^.PktFile,HoldFileName(TmpSP^.PktAdr,True)+'PTM'); FileMode:=ShareRW+ShareDenyRW;
  679.               TmpSP^.PktBufPos:=0;
  680. {$IFDEF DPMI}
  681.               IF MaxAvail>1024*1024 THEN TmpSp^.PktBufSiz:=65521 ELSE
  682. {$ENDIF}
  683.                 IF MaxAvail>256000 THEN TmpSP^.PktBufSiz:=20480 ELSE
  684.                   IF MaxAvail>204800 THEN TmpSP^.PktBufSiz:=16384 ELSE
  685.                     IF MaxAvail>153600 THEN TmpSP^.PktBufSiz:=12288 ELSE
  686.                       IF MaxAvail>102400 THEN TmpSP^.PktBufSiz:=8192 ELSE
  687.                         IF MaxAvail>30720 THEN TmpSP^.PktBufSiz:=4096 ELSE
  688.                           TmpSP^.PktBufSiz:=0;
  689.               IF TmpSP^.PktBufSiz>0 THEN GetMem(TmpSP^.PktBuf,TmpSp^.PktBufSiz);
  690.               INC(PktBufCount);
  691.               UsedMemStatus;
  692.               Reset(TmpSP^.PktFile,1);
  693.               IF IOResult=0 THEN
  694.               BEGIN
  695.                 Seek(TmpSP^.PktFile,FileSize(TmpSP^.PktFile)-1);
  696.                 IF IOResult<>0 THEN
  697.                 BEGIN
  698.                   SEEK(TmpSp^.PktFile,0);
  699.                   GOTO StrangeNulPkt;
  700.                 END;
  701.               END
  702.               ELSE
  703.               BEGIN
  704.                 REWRITE(TmpSP^.PktFile,1);
  705. StrangeNulpkt:
  706.                 FillOutPktHeader(Cfg.Addresses[CurrentAKA],SendToTabType(TmpAreas^.SendTo[j]^)[i],ph);
  707.                 AddToPktBuffer(TmpSP,ph,SizeOf(ph));
  708.               END;
  709.             END;
  710.             WITH pmh,SendToTabType(TmpAreas^.SendTo[j]^)[i] DO
  711.             BEGIN
  712.               destnet:=Net;
  713.               destnode:=Node;
  714.             END;
  715.             AddToPktBuffer(TmpSP,pmhByte[1],SizeOf(pmh));
  716.             s:=Msg^.WhoTo+#0+Msg^.WhoFrom+#0+Msg^.Subject+#0;
  717.             AddToPktBuffer(TmpSP,s[1],Length(s));
  718.             AddToPktBuffer(TmpSP,Msg^.TextBody[1],SeenByOffSet);
  719.             INC(MsgSentCount);
  720.           END;
  721.         END;
  722.       Message(LongIntForm('########',MsgSentCount),10);
  723.       IF TossIt THEN TossMessage;
  724.     END;
  725.  
  726.     PROCEDURE ScanTheMessage;
  727.     BEGIN
  728.       INC(TmpAreas^.Area.Scanned);
  729.       SetupPathAndSeenBy;
  730.       PrepareMessageBuffer(False);
  731.       WriteMessage(True);
  732.     END;
  733.  
  734.     FUNCTION ScanAllowed:Boolean;
  735.     BEGIN
  736.       WITH TmpAreas^.Area DO
  737.       BEGIN
  738.         IF ScanDate<>Today THEN
  739.         BEGIN
  740.           ScanDate:=Today;
  741.           Scanned:=0;
  742.         END;
  743.         ScanAllowed:=((MaxScan=0) OR (Scanned<MaxScan));
  744.       END;
  745.     END;
  746.  
  747.     PROCEDURE FindOffSets;
  748.     VAR
  749.       i:WORD;
  750.       s:STRING;
  751.       ss:S10;
  752.       ch:CHAR;
  753.     BEGIN
  754.       OriginOffSet:=0;
  755.       SeenByOffSet:=0;
  756.       PathOffSet:=0;
  757.       i:=Msg^.MsgLen-1;
  758.       s:='';
  759.       WHILE (i>0) AND ((Msg^.TextBody[i]=#10) OR (Msg^.TextBody[i]=#13)) DO
  760.         DEC(i);
  761.       IF i>0 THEN
  762.       BEGIN
  763.         s[0]:=#12;
  764.         s[13]:=#0;
  765.         REPEAT
  766.           REPEAT
  767.             DEC(i);
  768.           UNTIL (i=0) OR (Msg^.TextBody[i]=#13);
  769.           IF i>0 THEN
  770.           BEGIN
  771.             FillChar(s[1],12,0);
  772.             MoveFast(Msg^.TextBody[i+1],s[1],12);
  773.             ss:=COPY(Trim(s),1,4);
  774.             IF (s[1]<>#13) THEN
  775.               IF (ss='SEEN') THEN
  776.               BEGIN
  777.                 SeenByOffSet:=i+1;
  778.                 Continue;
  779.               END ELSE
  780.                 IF ss='PATH' THEN
  781.                 BEGIN
  782.                   PathOffSet:=i+1;
  783.                   Continue;
  784.                 END ELSE Break;
  785.           END ELSE Break;
  786.         UNTIL (i=0);
  787.         IF COPY(Trim(s),1,8)='* Origin' THEN OriginOffSet:=i+1;
  788.       END;
  789.     END;
  790.  
  791.     PROCEDURE ReadPathAndSeenBy;
  792.     VAR
  793.       l:BYTE;
  794.       lasti,i:WORD;
  795.       CurZone, CurNet:INTEGER;
  796.       s:STRING;
  797.       Flag:Boolean;
  798.       ch:CHAR;
  799.       ss:S10;
  800.  
  801.       PROCEDURE AddString(CONST as:STRING; VAR TabAdr; VAR Num:WORD);
  802.       VAR
  803.         Tab:SeenByTabType ABSOLUTE TabAdr;
  804.         s:STRING;
  805.         ss,sss:S30;
  806.         p:BYTE;
  807.         Flag:Boolean;
  808.         n,Test:INTEGER;
  809.         i:WORD;
  810.       BEGIN
  811.         IF s[LENGTH(s)]=#0 THEN DEC(s[0]);
  812.         s:=as+' ';
  813.         Flag:=False;
  814.         REPEAT
  815.           WHILE (s[1]=' ') AND (s<>'') DO
  816.             DELETE(s,1,1);
  817.           IF (s<>'') THEN
  818.           BEGIN
  819.             ss:=NextWord(' ',s);
  820.             p:=Pos('/',ss);
  821.             IF p>0 THEN
  822.             BEGIN
  823.               sss:=COPY(ss,1,p-1);
  824.               DELETE(ss,1,LENGTH(sss)+1);
  825.               VAL(sss,WORD(n),Test);
  826.               IF n<>CurNet THEN
  827.               BEGIN
  828.                 CurNet:=n;
  829.                 INC(Num,2);
  830.                 Tab[Num-1]:=-1;
  831.                 Tab[Num]:=CurNet;
  832.               END;
  833.             END;
  834.             INC(Num);
  835.             VAL(ss,Tab[Num],Test);
  836.           END ELSE
  837.             Flag:=True;
  838.         UNTIL (Flag);
  839.       END;
  840.  
  841.     BEGIN
  842.       NumPath:=0;
  843.       NumSeenBy:=0;
  844.       FillChar(SeenByTab^,SizeOf(SeenByTab^),0);
  845.       IF PathOffSet<>0 THEN
  846.       BEGIN
  847.         CurZone:=0; CurNet:=0;
  848.         i:=PathOffSet;
  849.         lasti:=i;
  850.         REPEAT
  851.           REPEAT
  852.             ch:=Msg^.TextBody[i];
  853.             INC(i);
  854.           UNTIL (ch=#13) OR (ch=#0);
  855.           IF ch<>#0 THEN
  856.           BEGIN
  857.             l:=i-lasti-1;
  858.             MoveFast(Msg^.TextBody[lasti],s[1],l);
  859.             s[0]:=CHAR(l);
  860.             s:=Trim(s);
  861.             IF COPY(s,1,4)='PATH' THEN
  862.             BEGIN
  863.               AddString(COPY(s,7,255),PathTab^,NumPath);
  864.               lasti:=i;
  865.               IF ch=#0 THEN Break;
  866.               Continue;
  867.             END ELSE Break;
  868.           END ELSE Break;
  869.         UNTIL False;
  870.       END;
  871.       IF SeenByOffSet<>0 THEN
  872.       BEGIN
  873.         CurZone:=0; CurNet:=0;
  874.         i:=SeenByOffSet;
  875.         IF OriginOffSet=0 THEN DEC(i);
  876.         lasti:=i;
  877.         REPEAT
  878.           IF i>PathOffSet-3 THEN Break;
  879.           REPEAT
  880.             ch:=Msg^.TextBody[i];
  881.             INC(i);
  882.           UNTIL (ch=#13) OR (ch=#0);
  883.           IF ch<>#0 THEN
  884.           BEGIN
  885.             l:=i-lasti-1;
  886.             MoveFast(Msg^.TextBody[lasti],s[1],l);
  887.             s[0]:=CHAR(l);
  888.             s:=Trim(s);
  889.             ss:=COPY(s,1,8);
  890.             IF (ss='SEEN-BY:') THEN
  891.             BEGIN
  892.               AddString(COPY(s,10,255),SeenByTab^,NumSeenBy);
  893.               lasti:=i;
  894.               IF ch=#0 THEN Break;
  895.               Continue;
  896.             END ELSE Break;
  897.           END ELSE Break;
  898.         UNTIL False;
  899.       END;
  900.     END;
  901.  
  902.     PROCEDURE CompleteCurrentMessage;
  903.     VAR
  904.       s:STRING;
  905.       i:BYTE;
  906.     BEGIN
  907.       s[0]:=#5;
  908.       MoveFast(Msg^.TextBody[1],s[1],5);
  909.       IF (s='AREA:') THEN
  910.       BEGIN
  911.         i:=6;
  912.         WHILE (Msg^.TextBody[i]=' ') DO
  913.           INC(i);
  914.         DEC(i);
  915.         REPEAT
  916.           INC(i);
  917.           IF Msg^.TextBody[i]<>#13 THEN Msg^.Tag:=Msg^.Tag+Msg^.TextBody[i];
  918.         UNTIL Msg^.TextBody[i]=#13;
  919.         TagEndPos:=i;
  920.       END ELSE
  921.         TagEndPos:=0;
  922.       FindOffsets; { initier SeenByOffset, Path offset, og OriginOffset }
  923.       IF SeenByOffSet>0 THEN RealMsgLen:=SeenByOffSet
  924.                         ELSE RealMsgLen:=Msg^.MsgLen;
  925.       ReadPathAndSeenBy; { Indlæs PathTab & SeenByTab }
  926.     END;
  927.  
  928.     FUNCTION AreaExists:Boolean;
  929.     VAR
  930.       b:Boolean;
  931.     BEGIN
  932.       b:=False;
  933.       TmpAreas:=AreasBBS;
  934.       ScannerAreaNum:=1;
  935.       WHILE (TmpAreas<>NIL) AND NOT b DO
  936.       BEGIN
  937.         IF TmpAreas^.Area.EchoNames[1]^=Msg^.Tag THEN b:=True ELSE
  938.         BEGIN
  939.           TmpAreas:=TmpAreas^.Next;
  940.           INC(ScannerAreaNum);
  941.         END;
  942.       END;
  943.       IF NOT b THEN TmpAreas:=NIL;
  944.       AreaExists:=b;
  945.     END;
  946.  
  947.     PROCEDURE ProcessPktFiles(CONST NodeStat: TNodeStat);
  948.     VAR
  949.       Ni:TNodeInfo;
  950.       sr:SEARCHREC;
  951.  
  952.       FUNCTION GetPktChar:CHAR;
  953.       BEGIN
  954.         INC(BufPos);
  955.         IF (BufPos>InBuffer) THEN
  956.         BEGIN
  957.           FillChar(Buffer^,MaxPktBuffer,0);
  958.           PktFile.BLOCKREADNum(Buffer^,MaxPktBuffer,InBuffer);
  959.           BufPos:=1;
  960.         END;
  961.         IF BufPos>InBuffer THEN PktEnd:=True;
  962.         GetPktChar:=CHAR(BT(Buffer^)[BufPos]);
  963.       END;
  964.  
  965.       PROCEDURE BuildMessages;
  966.       LABEL
  967.         StartAfterError;
  968.       VAR
  969.         i:WORD;
  970.         b:CHAR;
  971.  
  972.         PROCEDURE PolyMorphTimeStamp;
  973.         VAR
  974.           s,ss:S20;
  975.           ch:CHAR;
  976.         BEGIN
  977.           IF pmh.time[19]=#0 THEN
  978.           BEGIN
  979.             ss[0]:=#18;
  980.             MoveFast(pmh.time,ss[1],18);
  981.             INSERT(' ',ss,10);
  982.             ss:=ss+#0;
  983.             MoveFast(ss[1],pmh.time,20);
  984.           END ELSE
  985.           BEGIN
  986.             ch:=pmh.time[1];
  987.             IF NOT ((ch=' ') OR ((ch>='0') AND (ch<='9'))) THEN
  988.             BEGIN
  989.               s[0]:=#19;
  990.               MoveFast(pmh.time,s[1],19);
  991.               DELETE(s,1,4);
  992.               ss:=COPY(s,1,10)+' '+COPY(s,11,5)+':00'+#0;
  993.               MoveFast(ss[1],pmh.time,20);
  994.             END;
  995.           END;
  996.         END;
  997.  
  998.       BEGIN
  999.         PktError:=False;
  1000.         PktEnd:=False;
  1001.         REPEAT
  1002.           FillChar(pmh,SizeOf(Pmh),0);
  1003.           i:=0;
  1004. StartAfterError:
  1005.           REPEAT
  1006.             b:=GetPktChar;
  1007.             INC(i);
  1008.             PMHbyte[i]:=BYTE(b);
  1009.           UNTIL ((i>SIZEOF(pmh)-4) AND (b=#0)) OR (i>=SIZEOF(pmh)) OR Pktend;
  1010.           PolymorphTimeStamp;
  1011.           WITH pmh DO
  1012.           BEGIN
  1013.             CASE StartMsg OF
  1014.               0 : Exit;  {end of pkt}
  1015.               2 : BEGIN
  1016.                     INC(MsgScannedCount);
  1017.                     Message(LongIntForm('########',MsgScannedCount),8);
  1018.                     FillChar(Msg^,SizeOf(Msg^),0);
  1019.                     Msg^.OrigNode:=OrigNode;
  1020.                     Msg^.DestNode:=DestNode;
  1021.                     Msg^.OrigNet:=OrigNet;
  1022.                     Msg^.DestNet:=DestNet;
  1023.                     { Get "to name" }
  1024.                     b:=GetPktChar;
  1025.                     WHILE b<>#0 DO
  1026.                     BEGIN
  1027.                       Msg^.WhoTo:=Msg^.WhoTo+b;
  1028.                       b:=GetPktChar;
  1029.                     END;
  1030.                     { Get "from name" }
  1031.                     b:=GetPktChar;
  1032.                     WHILE b<>#0 DO
  1033.                     BEGIN
  1034.                       Msg^.WhoFrom:=Msg^.WhoFrom+b;
  1035.                       b:=GetPktChar;
  1036.                     END;
  1037.                     { Get "subject" }
  1038.                     b:=GetPktChar;
  1039.                     WHILE b<>#0 DO
  1040.                     BEGIN
  1041.                       Msg^.Subject:=Msg^.Subject+b;
  1042.                       b:=GetPktChar;
  1043.                     END;
  1044.                     b:=GetPktChar;
  1045.                     WHILE b<>#0 DO
  1046.                     BEGIN
  1047.                       INC(Msg^.MsgLen);
  1048.                       Msg^.TextBody[Msg^.MsgLen]:=b;
  1049.                       b:=GetPktChar;
  1050.                     END;
  1051.                     { Parse for area tag ( if any ) }
  1052.                     CompleteCurrentMessage;
  1053.                     { Process message here }
  1054.                     IF Msg^.Tag<>'' THEN { Wonderful, echo mail }
  1055.                     BEGIN
  1056.                       IF AreaExists THEN
  1057.                       BEGIN { All right, vi kender din slags!! }
  1058.                         IF NOT IsADupeMsg THEN { Ok, så du er altså ny her?? }
  1059.                         BEGIN
  1060.                           IF SecurityOK AND ScanAllowed THEN ScanTheMessage ELSE
  1061.                           BEGIN
  1062.                             TossIntoArea(Cfg.MailScanner.BadMsgs,BadMsgNum,1);
  1063.                             INC(MsgBadCount);
  1064.                             Message(LongIntForm('########',MsgBadCount),14);
  1065.                           END;
  1066.                         END ELSE
  1067.                         BEGIN
  1068.                           TossIntoArea(Cfg.MailScanner.SaveDupesDir,DupeMsgNum,1);
  1069.                           INC(MsgDupeCount);
  1070.                           Message(LongIntForm('########',MsgDupeCount),16);
  1071.                         END;
  1072.                       END ELSE
  1073.                       BEGIN { Orphan message, no known area }
  1074.                         TossIntoArea(Cfg.MailScanner.BadMsgs,BadMsgNum,1);
  1075.                         INC(MsgBadCount);
  1076.                         Message(LongIntForm('########',MsgBadCount),14);
  1077.                       END;
  1078.                     END ELSE
  1079.                     BEGIN { The damn thing is a matrix }
  1080.                       TossIntoArea(Cfg.MailScanner.NetMailDir,MatrixNum,1);
  1081.                     END;
  1082.                   END;
  1083.               ELSE
  1084.               BEGIN
  1085.                 REPEAT
  1086.                   b:=GetPktChar;
  1087.                   pmhbyte[1]:=BYTE(b);
  1088.                 UNTIL (PktEnd) OR (b=#2);
  1089.                 IF PktEnd THEN
  1090.                 BEGIN
  1091.                   Message('Error in PKT - aborting',2);
  1092.                   PktError:=True;
  1093.                 END ELSE
  1094.                 BEGIN
  1095.                   i:=1;
  1096.                   GOTO StartAfterError;
  1097.                 END;
  1098.                 Exit;
  1099.               END;
  1100.             END;
  1101.           END;
  1102.         UNTIL False;
  1103.       END;
  1104.  
  1105.     BEGIN
  1106.       FindFirst('*.PKT',AnyFile,Sr);
  1107.       WHILE DOSError=0 DO
  1108.       BEGIN
  1109.         Message(sr.name,4);
  1110.         PktFile.OpenWithMode(Sr.Name, 1, False, ShareRW+ShareDenyW);
  1111.         PktFile.BlockReadNum(Buffer^, SizeOf(TPktHeader), InBuffer);
  1112.         IF InBuffer=SizeOf(TPktHeader) THEN
  1113.         BEGIN
  1114.           GetPktHeadInfo(TPktHeader(Buffer^),PktOrig,PktDest);
  1115.           FindNodeInfo(Ni,PktOrig);
  1116.           IF Trim(StUpCase(AsciiZ2Str(TPktHeader(Buffer^).PassWord,7)))=Ni.PktPassWord THEN
  1117.           BEGIN
  1118.             Message('Tossing mail packet from '+Address2Str(PktOrig),2);
  1119.             BufPos:=0;
  1120.             InBuffer:=0;
  1121.             BuildMessages;
  1122.           END
  1123.           ELSE
  1124.           BEGIN
  1125.             PktError:=True;
  1126.             AddLog('!','Packet '+sr.name+' has invalid password. Us="'+Ni.PktPassWord+
  1127.                        '" Remote="'+StUpCase(AsciiZ2Str(TPktHeader(Buffer^).PassWord,7))+'"');
  1128.           END;
  1129.         END ELSE
  1130.           Message(Sr.Name+' is a short packet - deleting',2);
  1131.         PktFile.Close;
  1132.         IF NOT PktError THEN
  1133.           DeleteFile(Sr.Name)
  1134.         ELSE
  1135.           CopyFile(Cfg.Inbound[NodeStat]+sr.name,Cfg.FwdFile.SecureDir+sr.name,False,True);
  1136.         FindNext(Sr);
  1137.       END;
  1138.       FindClose(Sr);
  1139.     END;
  1140.  
  1141.     PROCEDURE DisposePktFiles;
  1142.     VAR
  1143.       tmp:SendPktPtr;
  1144.       ch:CHAR;
  1145.       BusyFile:FILE;
  1146.     BEGIN
  1147.       ch:=#0;
  1148.       WHILE SendPkt<>NIL DO
  1149.       BEGIN
  1150.         Tmp:=SendPkt;
  1151.         SendPkt:=SendPkt^.Next;
  1152.         AddToPktBuffer(Tmp,ch,1);
  1153.         FlushPkt(Tmp);
  1154.         Close(Tmp^.PktFile);
  1155.         IF MarkNodeBusy(BusyFile,Tmp^.PktAdr) THEN
  1156.         BEGIN
  1157.           RENAME(Tmp^.PktFile,HoldFileName(Tmp^.PktAdr,True)+'OUT');
  1158.           InOutRes:=0;
  1159.           UnMarkNodeBusy(BusyFile);
  1160.         END;
  1161.         IF Tmp^.PktBufSiz>0 THEN FreeMem(Tmp^.PktBuf,Tmp^.PktBufSiz);
  1162.         Dispose(Tmp);
  1163.       END;
  1164.     END;
  1165.  
  1166.     FUNCTION MailExt(Day:BYTE):S2;
  1167.     BEGIN
  1168.       MailExt:=StUpCase(COPY(DayString[DayType(Day)],1,2));
  1169.     END;
  1170.  
  1171.     PROCEDURE ScanBadMsgs;
  1172.     VAR
  1173.       p : Pointer;
  1174.       i : WORD;
  1175.       TxtLen: LongInt;
  1176.       h:MsgHdrType;
  1177.  
  1178.     BEGIN
  1179.       Message('Scanning BAD message area',2);
  1180.       FOR i:=1 TO GetHighestMsg(Cfg.MailScanner.BadMsgs) DO
  1181.       BEGIN
  1182.         IF ReadMsg(Cfg.MailScanner.BadMsgs,i,h,TxtLen,p) THEN
  1183.         BEGIN
  1184.           Message(Long2Str(i)+'.MSG',4);
  1185.           OpusMsgToMSMsg(h,p,TxtLen,Msg);
  1186.           WITH pmh DO
  1187.           BEGIN
  1188.             startmsg:=2;
  1189.             orignode:=Msg^.OrigNode;
  1190.             destnode:=Msg^.DestNode;
  1191.             orignet:=Msg^.OrigNet;
  1192.             destnet:=Msg^.DestNet;
  1193.             attr:=h.attribute;
  1194.             cost:=h.cost;
  1195.             MoveFast(h.datetime,pmh.time,20);
  1196.           END;
  1197.           CompleteCurrentMessage;
  1198.           IF AreaExists THEN
  1199.           BEGIN
  1200.             IF SecurityOK AND ScanAllowed THEN
  1201.             BEGIN
  1202.               IsADupeMsg;
  1203.               ScanTheMessage;
  1204.               DeleteFile(Cfg.MailScanner.BadMsgs+Long2Str(i)+'.MSG');
  1205.             END;
  1206.           END;
  1207.           FreeMemCheck(p,TxtLen);
  1208.         END;
  1209.       END;
  1210.       Message('',2);
  1211.     END;
  1212.  
  1213.     FUNCTION QBbsTime2MsgTime(VAR m:HudsonHdrRecord):S20;
  1214.     VAR
  1215.       s:S20;
  1216.     BEGIN
  1217.       s:=DateToDateString('dd nnn yy  ',DateStringToDate('mm-dd-yy',m.PostDate))+
  1218.          TimeToTimeString('hh:mm',TimeStringToTime('hh:mm',m.PostTime))+':00'#0;
  1219.       QBbsTime2MsgTime:=s;
  1220.     END;
  1221.  
  1222.     FUNCTION FindAreaTag(CONST Loc: PathStr): S40;
  1223.     VAR
  1224.       ss,s:PathStr;
  1225.       p:BYTE;
  1226.     BEGIN
  1227.       s:='';
  1228.       TmpAreas:=AreasBBS;
  1229.       WHILE (TmpAreas<>NIL) AND (s='') DO
  1230.       BEGIN
  1231.         ss:=TmpAreas^.Area.Directory^;
  1232.         p:=POS('\',ss);
  1233.         WHILE p>0 DO
  1234.         BEGIN
  1235.           DELETE(ss,1,p);
  1236.           p:=POS('\',ss);
  1237.         END;
  1238.         IF ss=Loc THEN s:=TmpAreas^.Area.EchoNames[1]^;
  1239.         TmpAreas:=TmpAreas^.Next;
  1240.       END;
  1241.       FindAreaTag:=s;
  1242.     END;
  1243.  
  1244.     PROCEDURE ImportNetMail;
  1245.     VAR
  1246.       x,i:WORD;
  1247.       l : LongInt;
  1248.       Test:INTEGER;
  1249.       p:Pointer;
  1250.       h:MsgHdrType;
  1251.       Orig,Adr:TFidoAddress;
  1252.       s:STRING;
  1253.     BEGIN
  1254.       IF (Cfg.BBS.BBSType IN [btQBBS, btRA, btSBBS]) AND (Cfg.MailScanner.NetMailBoard>0) THEN
  1255.       BEGIN
  1256.         Message('Importing messages from external netmail area',2);
  1257.         FOR i:=1 TO GetHighestMsg(Cfg.MailScanner.NetMailDir) DO
  1258.         BEGIN
  1259.           Message('Checking msg #'+Long2Str(i),4);
  1260.           IF ReadMsg(Cfg.MailScanner.NetMailDir,i,h,l,p) THEN
  1261.           BEGIN
  1262.             s[0]:=#5;
  1263.             MoveFast(p^,s[1],5);
  1264.             IF s<>'AREA:' THEN
  1265.             BEGIN
  1266.               FindMsgAdr(h,p,l,Orig,Adr);
  1267.               IF IsOurAddress(Adr) THEN
  1268.               BEGIN
  1269.                 Message('Importing msg #'+Long2Str(i)+' From '+Address2Str(Orig),4);
  1270.                 AddLog('#','Importing msg #'+Long2Str(i)+' From '+Address2Str(Orig));
  1271.                 WITH QBase^ DO
  1272.                 BEGIN
  1273.                   FillChar(MsgHdr,SizeOf(MsgHdr),0);
  1274.                   WITH MsgHdr DO
  1275.                   BEGIN
  1276.                     Cost:=pmh.Cost;
  1277.                     IF h.Attribute AND MsgPrivate<>0 THEN MsgAttr:=MsgAttr OR qbPrivate;
  1278.                     IF h.Attribute AND MsgRead   <>0 THEN MsgAttr:=MsgAttr OR qbReceived;
  1279.                     IF h.Attribute AND MsgSent   <>0 THEN MsgAttr:=MsgAttr OR qbSent;
  1280.                     IF h.Attribute AND MsgFile   <>0 THEN MsgAttr:=MsgAttr OR qbFileAttach;
  1281.                     PostTime[0]:=#5;
  1282.                     MoveFast(h.DateTime[11],PostTime[1],5);
  1283.                     s[0]:=#9;
  1284.                     MoveFast(h.DateTime[0],s[1],9);
  1285.                     PostDate:=DateToDateString('mm-dd-yy',DateStringToDate('dd nnn yy',s));
  1286.                     DestZone:=Cfg.Addresses[CurrentAKA].Zone;
  1287.                     OrigZone:=Cfg.Addresses[CurrentAKA].Zone;
  1288.                     DestNet:=h.DestNet;
  1289.                     DestNode:=h.DestNode;
  1290.                     OrigNet:=h.OrigNet;
  1291.                     OrigNode:=h.OrigNode;
  1292.                     WhoTo:=AsciiZ2Str(h.ToUser,36);
  1293.                     WhoFrom:=AsciiZ2Str(h.FromUser,36);
  1294.                     Subj:=AsciiZ2Str(h.Subject,72);
  1295.                   END;
  1296.                 END;
  1297.                 WriteHudsonMessage(p^,0,l,Cfg.MailScanner.NetMailBoard);
  1298.                 DeleteFile(Cfg.MailScanner.NetMailDir+Long2Str(i)+'.MSG');
  1299.               END;
  1300.             END;
  1301.             FreeMemCheck(p,l);
  1302.           END;
  1303.         END;
  1304.       END;
  1305.       Message('',2);
  1306.     END;
  1307.  
  1308.     PROCEDURE ScanMessageBase;
  1309.  
  1310.       PROCEDURE ScanQBBSMessageBase;
  1311.       VAR
  1312.         i,n:WORD;
  1313.         s:STRING;
  1314.  
  1315.         PROCEDURE ExportNetMail;
  1316.         VAR
  1317.           i,j:WORD;
  1318.           l: LongInt;
  1319.           s:S20;
  1320.           h:MsgHdrType;
  1321.           p:Pointer;
  1322.           orig,dest:TFidoAddress;
  1323.         BEGIN
  1324.           FillChar(Msg^,SizeOf(Msg^),0);
  1325.           FillChar(pmh,SizeOf(pmh),0);
  1326.           WITH Msg^,QBase^ DO
  1327.           BEGIN
  1328.             s:=QbbsTime2MsgTime(MsgHdr);
  1329.             MoveFast(s[1],pmh.time,20);
  1330.             WhoTo:=MsgHdr.WhoTo;
  1331.             WhoFrom:=MsgHdr.WhoFrom;
  1332.             Subject:=MsgHdr.Subj;
  1333.             pmh.OrigNet:=MsgHdr.OrigNet;
  1334.             pmh.OrigNode:=MsgHdr.OrigNode;
  1335.             pmh.DestNet:=MsgHdr.DestNet;
  1336.             pmh.DestNode:=MsgHdr.DestNode;
  1337.             pmh.cost:=MsgHdr.Cost;
  1338.             MsgTxtFile.SEEK(MsgHdr.StartRec);
  1339.             FOR i:=1 TO MsgHdr.NumRecs DO
  1340.             BEGIN
  1341.               MsgTxtFile.Read(MsgTxtTab[1],nokeep,wait);
  1342.               FOR j:=1 TO LENGTH(MsgTxtTab[1]) DO
  1343.                 IF MsgTxtTab[1][j]<>#0 THEN
  1344.                 BEGIN
  1345.                   INC(MsgLen);
  1346.                   TextBody[MsgLen]:=MsgTxtTab[1][j];
  1347.                 END;
  1348.             END;
  1349.  
  1350.             pmh.attr:=0;
  1351.  
  1352.             IF MsgHdr.MsgAttr AND qbPrivate<>0  THEN pmh.attr:=pmh.attr OR MsgPrivate;
  1353.             IF MsgHdr.MsgAttr AND qbReceived<>0 THEN pmh.attr:=pmh.attr OR MsgRead;
  1354.             IF MsgHdr.MsgAttr AND qbLocal<>0    THEN pmh.attr:=pmh.attr OR MsgLocal;
  1355.  
  1356.             IF MsgHdr.NetAttr AND qbKill<>0       THEN pmh.attr:=pmh.attr OR MsgKill;
  1357.             IF MsgHdr.NetAttr AND qbFileAttach<>0 THEN pmh.attr:=pmh.attr OR MsgFile;
  1358.             IF MsgHdr.NetAttr AND qbCrash<>0      THEN pmh.attr:=pmh.attr OR MsgCrash;
  1359.             IF MsgHdr.NetAttr AND qbReqRcpt<>0    THEN pmh.attr:=pmh.attr OR MsgRcpt;
  1360.             IF MsgHdr.NetAttr AND qbAuditReq<>0   THEN pmh.attr:=pmh.attr OR MsgAReq;
  1361.             IF MsgHdr.NetAttr AND qbReturnRcpt<>0 THEN pmh.attr:=pmh.attr OR MsgRReq;
  1362.  
  1363.             TossIntoArea(Cfg.MailScanner.NetMailDir,MatrixNum,1);
  1364.             MsgHdr.MsgAttr:=MsgHdr.MsgAttr XOR qbSent;
  1365.             MsgHdr.NetAttr:=MsgHdr.NetAttr XOR qbSent;
  1366.             IF MsgHdr.NetAttr AND qbKill<>0 THEN
  1367.             BEGIN
  1368.               MsgHdr.MsgAttr:=MsgHdr.MsgAttr OR 1;
  1369.               MsgToIdx:='* Deleted *';
  1370.               MsgToIdxFile.PutRec(MsgToIdx,MsgHdrFile.FILEPOS-1);
  1371.               MsgIdxFile.GetRec(MsgIdx,MsgHdrFile.FILEPOS-1,Keep,Wait);
  1372.               MsgIdx.MsgNum:=-1;
  1373.               MsgIdxFile.PutRec(MsgIdx,MsgHdrFile.FILEPOS-1);
  1374.               IF MsgInfo.TotalActive>0 THEN DEC(MsgInfo.TotalActive);
  1375.               IF MsgInfo.ActiveMsgs[MsgHdr.Board]>0 THEN DEC(MsgInfo.ActiveMsgs[MsgHdr.Board]);
  1376.               MsgInfoFile.PutRec(MsgInfo,0);
  1377.             END;
  1378.             MsgHdrFile.PutRec(MsgHdr,MsgHdrFile.FILEPOS-1);
  1379.             IF ReadMsg(Cfg.MailScanner.NetMailDir,MatrixNum,h,l,p) THEN
  1380.             BEGIN
  1381.               FindMsgAdr(h,p,l,Orig,Dest);
  1382.               AddLog('#','Exporting net mail from '+Address2Str(Orig)+' to '+Address2Str(Dest));
  1383.               FreeMemCheck(p,l);
  1384.             END;
  1385.           END;
  1386.         END;
  1387.  
  1388.       BEGIN
  1389.         WITH QBase^ DO
  1390.         BEGIN
  1391.           WHILE NOT MsgHdrFile.EOF DO
  1392.           BEGIN
  1393.             MsgHdrFile.Read(MsgHdr,NoKeep,Wait);
  1394.             IF (MsgHdr.MsgAttr AND 7)=6 THEN
  1395.               ExportNetMail
  1396.             ELSE
  1397.               IF MsgHdr.MsgAttr AND 33=32 THEN
  1398.               BEGIN
  1399.                 { Send message here }
  1400.                 FillChar(Msg^,SizeOf(Msg^),0);
  1401.                 Msg^.Tag:=FindAreaTag(Long2Str(MsgHdr.Board));
  1402.                 IF AreaExists THEN
  1403.                 BEGIN
  1404.                   SetAKA;
  1405.                   FillChar(pmh,SIZEOF(pmh),0);
  1406.                   WITH Pmh DO
  1407.                   BEGIN
  1408.                     startmsg:=2;
  1409.                     orignode:=MsgHdr.OrigNode;
  1410.                     destnode:=MsgHdr.DestNode;
  1411.                     orignet:=MsgHdr.OrigNet;
  1412.                     destnet:=MsgHdr.DestNet;
  1413.                     IF MsgHdr.MsgAttr AND 8=8   THEN attr:=attr OR MsgPrivate;
  1414.                     IF MsgHdr.MsgAttr AND 16=16 THEN attr:=attr OR MsgRead;
  1415.                     cost:=MsgHdr.Cost;
  1416.                     s:=QBbsTime2MsgTime(MsgHdr);
  1417.                     MoveFast(s[1],time,20);
  1418.                     WITH MsgHdr DO
  1419.                     BEGIN
  1420.                       Msg^.WhoTo:=WhoTo;
  1421.                       Msg^.WhoFrom:=WhoFrom;
  1422.                       Msg^.Subject:=Subj;
  1423.                     END;
  1424.                   END;
  1425.                   IF NOT IsADupeMsg THEN
  1426.                   BEGIN
  1427.                     WITH MsgHdr DO
  1428.                     BEGIN
  1429.                       OrigNet:=Cfg.Addresses[CurrentAKA].Net;
  1430.                       OrigNode:=Cfg.Addresses[CurrentAKA].Node;
  1431.                       s:='AREA:'+Msg^.Tag+#13;
  1432.                       MoveFast(s[1],Msg^.TextBody,LENGTH(s));
  1433.                       Msg^.MsgLen:=LENGTH(s);
  1434.                       MsgTxtFile.SEEK(StartRec);
  1435.                       FOR n:=1 TO NumRecs DO
  1436.                       BEGIN
  1437.                         MsgTxtFile.Read(MsgTxtTab[1],NoKeep,Wait);
  1438.                         FOR i:=1 TO LENGTH(MsgTxtTab[1]) DO
  1439.                           IF MsgTxtTab[1][i]<>#0 THEN
  1440.                           BEGIN
  1441.                             INC(Msg^.MsgLen);
  1442.                             Msg^.TextBody[Msg^.MsgLen]:=MsgTxtTab[1][i];
  1443.                           END;
  1444.                       END;
  1445.                     END;
  1446.                     INC(Msg^.MsgLen);
  1447.                     InitPathAndSeenBy;
  1448.                     PrepareMessageBuffer(False);
  1449.                     WriteMessage(False);
  1450.                   END;
  1451.                   { Mark message as sent }
  1452.                   MsgHdr.MsgAttr:=MsgHdr.MsgAttr AND 255-32;
  1453.                   MsgHdrFile.PutRec(MsgHdr,MsgHdrFile.FILEPOS-1);
  1454.                 END;
  1455.               END;
  1456.           END;
  1457.         END;
  1458.         DeleteFile(BasePath+'NETMAIL.BBS');
  1459.         DeleteFile(BasePath+'ECHOMAIL.BBS');
  1460.       END;
  1461.  
  1462.       PROCEDURE ScanMsgMessageBase;
  1463.       CONST
  1464.         HWMTekst='PORTAL ECHO MAIL SYSTEM';
  1465.       VAR
  1466.         ss,s:PathStr;
  1467.         Last,i:WORD;
  1468.         Len : LongInt;
  1469.         h:MsgHdrType;
  1470.         p:Pointer;
  1471.  
  1472.         FUNCTION ScanStart(CONST Path: PathStr): WORD;
  1473.         VAR
  1474.           Len: LongInt;
  1475.           p:Pointer;
  1476.           h:MsgHdrType;
  1477.         BEGIN
  1478.           ScanStart:=1;
  1479.           IF ReadMsg(Path,1,h,Len,p) THEN
  1480.           BEGIN
  1481.             IF AsciiZ2Str(h.ToUser,36)=HWMTekst THEN ScanStart:=h.ReplyTo;
  1482.             FreeMemCheck(p,Len);
  1483.           END;
  1484.         END;
  1485.  
  1486.         PROCEDURE SetHWM(CONST Path: PathStr; Num:WORD);
  1487.         VAR
  1488.           Len:WORD;
  1489.           h:MsgHdrType;
  1490.         BEGIN
  1491.           FillChar(h,SIZEOF(h),0);
  1492.           h.Attribute:=MsgPrivate+MsgRead+MsgSent;
  1493.           Str2AsciiZ(HWMTekst,h.ToUser,36);
  1494.           h.ReplyTo:=Num;
  1495.           WriteMsg(Path,1,h,0,NIL);
  1496.         END;
  1497.  
  1498.       BEGIN
  1499.         TmpAreas:=AreasBBS;
  1500.         WHILE (TmpAreas<>NIL) DO
  1501.         BEGIN
  1502.           IF TmpAreas^.Area.Directory^<>'' THEN
  1503.           BEGIN
  1504.             SetAKA;
  1505.             s:=TmpAreas^.Area.Directory^;
  1506.             Last:=GetHighestMsg(s);
  1507.             FOR i:=ScanStart(s) TO Last DO
  1508.             BEGIN
  1509.               IF ReadMsg(s,i,h,Len,p) THEN
  1510.               BEGIN
  1511.                 IF h.Attribute AND MsgSent=0 THEN
  1512.                 BEGIN
  1513.                   OpusMsgToMSMsg(h,p,Len,Msg);
  1514.                   Msg^.Tag:=TmpAreas^.Area.EchoNames[1]^;
  1515.                   IF NOT IsADupeMsg THEN
  1516.                   BEGIN
  1517.                     ss:='AREA:'+TmpAreas^.Area.EchoNames[1]^+#13;
  1518.                     MoveFast(Msg^.TextBody,Msg^.TextBody[LENGTH(ss)+1],Msg^.MsgLen);
  1519.                     MoveFast(ss[1],Msg^.TextBody,LENGTH(ss));
  1520.                     INC(Msg^.MsgLen,LENGTH(ss));
  1521.                     InitPathAndSeenBy;
  1522.                     SetupPathAndSeenBy;
  1523.                     PrepareMessageBuffer(False);
  1524.                     DEC(SeenByOffSet);
  1525.                     WITH pmh DO
  1526.                     BEGIN
  1527.                       StartMsg:=2;
  1528.                       orignode:=h.orignode;
  1529.                       destnode:=h.destnode;
  1530.                       orignet:=h.orignet;
  1531.                       destnet:=h.destnet;
  1532.                       attr:=h.attribute;
  1533.                       cost:=h.cost;
  1534.                       MoveFast(h.datetime,pmh.time,20);
  1535.                     END;
  1536.                     WriteMessage(False);
  1537.                   END;
  1538.                   h.Attribute:=h.Attribute OR MsgSent;
  1539.                   WriteMsg(s,i,h,Len,p);
  1540.                 END;
  1541.  
  1542.                 FreeMemCheck(p,Len);
  1543.               END;
  1544.               SetHWM(TmpAreas^.Area.Directory^,Last);
  1545.             END;
  1546.           END;
  1547.           TmpAreas:=TmpAreas^.Next;
  1548.         END;
  1549.       END;
  1550.  
  1551.     BEGIN
  1552.       Message('Scanning for outgoing mail',2);
  1553.       CASE Cfg.BBS.BBSType OF
  1554.         btQBBS,btRA,btSBBS        : ScanQBBSMessageBase;
  1555.         btOpus110,btOpus170,btMax : ScanMsgMessageBase;
  1556.       END;
  1557.     END;
  1558.  
  1559.     FUNCTION IsAMailBundle(CONST s: S12):Boolean;
  1560.     VAR
  1561.       l:LongInt;
  1562.       test:INTEGER;
  1563.     BEGIN
  1564.       IsAMailBundle:=False;
  1565.       IF LENGTH(s)<>12 THEN Exit;
  1566.       IF (s[12]<'0') OR (s[12]>'9') THEN Exit;
  1567.       VAL('$'+COPY(s,1,8),l,test);
  1568.       IF test<>0 THEN Exit;
  1569.       IsAMailBundle:=True;
  1570.     END;
  1571.  
  1572.     PROCEDURE CleanUpDupesDir;
  1573.     VAR
  1574.       Num,i:WORD;
  1575.       sr:SEARCHREC;
  1576.     BEGIN
  1577.       Message('Checking/cleaning dupes directory',2);
  1578.       Num:=0;
  1579.       FindFirst(Cfg.MailScanner.SaveDupesDir+'*.MSG',Archive,sr);
  1580.       WHILE DOSERROR=0 DO
  1581.       BEGIN
  1582.         INC(Num);
  1583.         FINDNEXT(sr);
  1584.       END;
  1585.       FindClose(sr);
  1586.       i:=0;
  1587.       WHILE Num>Cfg.MailScanner.MaxDupes DO
  1588.       BEGIN
  1589.         REPEAT
  1590.           INC(i);
  1591.         UNTIL DeleteFile(Cfg.MailScanner.SaveDupesDir+Long2Str(i)+'.MSG');
  1592.         DEC(Num);
  1593.       END;
  1594.     END;
  1595.  
  1596.     PROCEDURE WriteStat;
  1597.     VAR
  1598.       Tim:LongInt;
  1599.  
  1600.       FUNCTION Average(l:LongInt):S20;
  1601.       VAR
  1602.         s:S20;
  1603.       BEGIN
  1604.         Average:='';
  1605.         IF l<>0 THEN
  1606.         BEGIN
  1607.           STR(l/Tim:0:1,s);
  1608.           Average:=', '+s+'/second';
  1609.         END ELSE Average:='';
  1610.       END;
  1611.  
  1612.     BEGIN
  1613.       Tim:=StopMSTimer;
  1614.       AddLog('#','Mail scanner time '+Long2Str(Tim)+' second(s)');
  1615.       AddLog('#','Scanned '+Long2Str(MsgScannedCount)+' message(s)'+Average(MsgScannedCount));
  1616.       AddLog('#','Tossed '+Long2Str(MsgTossedCount)+' message(s)'+Average(MsgTossedCount));
  1617.       AddLog('#','Sent '+Long2Str(MsgSentCount)+' message(s). '+Average(MsgSentCount));
  1618.     END;
  1619.  
  1620.     FUNCTION IsOurFile(CONST FName: PathStr):Boolean;
  1621.     VAR
  1622.       f:FILE;
  1623.     BEGIN
  1624.       IsOurFile:=False;
  1625.       Assign(f,FName); FileMode:=ShareRW; Reset(f,1);
  1626.       IF IOResult=0 THEN Close(f) ELSE Exit;
  1627.       IsOurFile:=True;
  1628.     END;
  1629.  
  1630.     PROCEDURE SaveAllMemDupes;
  1631.     VAR
  1632.       i:BYTE;
  1633.     BEGIN
  1634.       i:=0;
  1635.       REPEAT
  1636.         INC(i);
  1637.         IF Dupe[i]^.d.Tag<>'' THEN
  1638.           DupeFile.PutRec(Dupe[i]^.d,Dupe[i]^.DFPos)
  1639.         ELSE
  1640.           Break;
  1641.       UNTIL (i=MaxDupeBases);
  1642.     END;
  1643.  
  1644.   BEGIN
  1645.     GetMem(Buffer, MaxPktBuffer);
  1646.     SendPkt:=NIL;
  1647.     ScanBadMsgs;
  1648.     IF Cfg.BBS.BBSType IN [btQBBS,btRA,btSBBS] THEN
  1649.     BEGIN
  1650.       QBase^.MsgInfoFile.Lock(1,wait);
  1651.       QBase^.MsgInfoFile.GetRec(QBase^.MsgInfo,0,NoKeep,Wait);
  1652.     END;
  1653.     GetDT(MSTimer);
  1654.     IF RunParametersType(GemRp).Toss THEN
  1655.     BEGIN
  1656.       FOR NodeStat:=nsUnKnown TO nsPassword DO
  1657.       BEGIN
  1658.         IF (Cfg.InboundToDo[NodeStat] AND itd_Mail)<>0 THEN
  1659.         BEGIN
  1660.           ChangeDir(Cfg.Inbound[NodeStat]);
  1661.           ProcessPktFiles(NodeStat);
  1662.           FOR i:=0 TO 6 DO
  1663.           BEGIN
  1664.             FindFirst('????????.'+MailExt(i)+'?',Archive,Sr);
  1665.             WHILE DOSError=0 DO
  1666.             BEGIN
  1667.               IF IsAMailBundle(sr.name) AND (IsOurFile(sr.name)) THEN
  1668.               BEGIN
  1669.                 Message('Unpacking mail bundle '+Sr.Name,2);
  1670.                 IF NOT ArcCommand(ArcType(Sr.Name),2,Sr.Name,'*.PKT') THEN
  1671.                 BEGIN
  1672.                   AddLog('!','Error unpacking '+sr.name+', moving it to '+Cfg.FwdFile.SecureDir);
  1673.                   CopyFile(Cfg.Inbound[NodeStat]+sr.name,Cfg.FwdFile.SecureDir+sr.name,False,True);
  1674.                   FINDFIRST(Cfg.Inbound[NodeStat]+'*.PKT',Archive,delsr);
  1675.                   WHILE DOSERROR=0 DO
  1676.                   BEGIN
  1677.                     DeleteFile(Cfg.Inbound[NodeStat]+DelSr.name);
  1678.                     FINDNEXT(DelSr);
  1679.                   END;
  1680.                   FindClose(DelSr);
  1681.                 END ELSE
  1682.                 BEGIN
  1683.                   DeleteFile(Cfg.Inbound[NodeStat]+sr.name);
  1684.                   ProcessPktFiles(NodeStat);
  1685.                 END;
  1686.               END;
  1687.               FindNext(Sr);
  1688.             END;
  1689.             FindClose(Sr);
  1690.           END;
  1691.         END;
  1692.       END;
  1693.       Message('Writing toss log',2);
  1694.       WriteEchoTossLog(AreasBBS);
  1695.       ChangeDir(StartPath);
  1696.       ScanNetMail;
  1697.       ImportNetMail;
  1698.       IF (Cfg.MailScanner.SaveDupesDir<>'') AND (Cfg.MailScanner.MaxDupes<>0) THEN
  1699.         CleanUpDupesDir;
  1700.     END;
  1701.     IF RunParametersType(GemRp).Scan THEN ScanMessageBase;
  1702.     IF Cfg.BBS.BBSType IN [btQBBS,btRA,btSBBS] THEN
  1703.       WITH QBase^ DO
  1704.       BEGIN
  1705.         MsgInfoFile.PutRec(MsgInfo,0);
  1706.         MsgInfoFile.UnLock(1);
  1707.       END;
  1708.     WriteStat;
  1709.     SaveAllMemDupes;
  1710.     DisposePktFiles;
  1711.     FreeMem(Buffer, MaxPktBuffer);
  1712.     Message('Updating Echo mail areas',2);
  1713.     DisposeAreasBBS(AreasBBS);
  1714.     DupeFile.Close;
  1715.     IF RunParametersType(GemRp).Pack THEN
  1716.     BEGIN
  1717.       Message('Packing and routing mail',2);
  1718.       Message('',4);
  1719.       Message('Net mail directory '+Cfg.MailScanner.NetMailDir,6);
  1720.       PerformPacking(RunParametersType(GemRp).Sched);
  1721.     END;
  1722.   END;
  1723.  
  1724.   PROCEDURE SetUpMessageBase;
  1725.   VAR
  1726.     i:WORD;
  1727.   BEGIN
  1728.     Message('Initializing message system',2);
  1729.     MsgScannedCount:=0;
  1730.     MsgSentCount:=0;
  1731.     MsgTossedCount:=0;
  1732.     MsgBadCount:=0;
  1733.     MsgDupeCount:=0;
  1734.     IF POS('\',AreasBBS^.Area.Directory^)>0 THEN
  1735.     BEGIN
  1736.       i:=LENGTH(AreasBBS^.Area.Directory^);
  1737.       WHILE AreasBBS^.Area.Directory^[i]<>'\' DO
  1738.         DEC(i);
  1739.       BasePath:=COPY(AreasBBS^.Area.Directory^,1,i);
  1740.     END ELSE
  1741.       BasePath:=StartPath;
  1742.     CASE Cfg.BBS.BBSType OF
  1743.       btQBBS,
  1744.       btRA,
  1745.       btSBBS : BEGIN
  1746.             New(QBase);
  1747.             WITH QBase^ DO
  1748.             BEGIN
  1749.               MsgInfoFile.Open(BasePath+'MSGINFO.BBS',SizeOf(HudsonInfoRecord),True);
  1750.               IF MsgInfoFile.FILESIZE=0 THEN
  1751.               BEGIN
  1752.                 FillChar(MsgInfo,SizeOf(HudsonInfoRecord),0);
  1753.                 MsgInfoFile.PutRec(MsgInfo,0);
  1754.               END ELSE
  1755.                 MsgInfoFile.GetRec(MsgInfo,0,NoKeep,Wait);
  1756.               MsgToIdxFile.Open(BasePath+'MSGTOIDX.BBS',SizeOf(S35),True);
  1757.               MsgIdxFile.Open(BasePath+'MSGIDX.BBS',SizeOf(HudsonIdxRecord),True);
  1758.               MsgHdrFile.Open(BasePath+'MSGHDR.BBS',SizeOf(HudsonHdrRecord),True);
  1759.               MsgTxtFile.Open(BasePath+'MSGTXT.BBS',SizeOf(S255),True);
  1760.             END;
  1761.           END;
  1762.       btOpus110,
  1763.       btOpus170,
  1764.       btMax : BEGIN
  1765.             New(OpusBase);
  1766.             FOR i:=1 TO 1000 DO
  1767.               OpusBase^[i]:=-1;
  1768.           END;
  1769.     END;
  1770.   END;
  1771.  
  1772.   PROCEDURE FinishMessageBase;
  1773.   BEGIN
  1774.     Message('Cleaning up',2);
  1775.     CASE Cfg.BBS.BBSType OF
  1776.       btQBBS,
  1777.       btRA,
  1778.       btSBBS : BEGIN
  1779.             WITH QBase^ DO
  1780.             BEGIN
  1781.               MsgToIdxFile.Close;
  1782.               MsgIdxFile.Close;
  1783.               MsgHdrFile.Close;
  1784.               MsgTxtFile.Close;
  1785.               MsgInfoFile.Close;
  1786.             END;
  1787.             Dispose(QBase);
  1788.           END;
  1789.       btOpus110,
  1790.       btOpus170,
  1791.       btMax : Dispose(OpusBase);
  1792.     END;
  1793.   END;
  1794.  
  1795. BEGIN
  1796.   CurrentAKA:=Cfg.MainAdrNum;
  1797.   GetEsr(EsrMailScanParams,2,Esr);
  1798.   Esr.SetWrapMode(ExitAtBot);
  1799.   rp:=Esr.GetUserRecord;
  1800.   IF Flags=0 THEN
  1801.   BEGIN
  1802.     RunParametersType(rp^).Toss:=(CurrentEvent.Typ AND etTossMail<>0);
  1803.     RunParametersType(rp^).Pack:=(CurrentEvent.Typ AND etPackMail<>0);
  1804.     RunParametersType(rp^).Scan:=(CurrentEvent.Typ AND etScanMail<>0);
  1805.     RunParametersType(rp^).Sched:=0;
  1806.     Esr.Process;
  1807.     Cmd:=Esr.GetLastCommand;
  1808.     GemRp:=RunParametersType(rp^);
  1809.     Esr.Done;
  1810.   END ELSE
  1811.   BEGIN
  1812.     Cmd:=ccDone;
  1813.     GemRp.Toss:=(Flags AND etTossMail<>0);
  1814.     GemRp.Pack:=(Flags AND etPackMail<>0);
  1815.     GemRp.Scan:=(Flags AND etScanMail<>0);
  1816.     GemRp.Sched:=CurrentEvent.SchedNumber;
  1817.   END;
  1818.   IF Cmd<>ccQuit THEN
  1819.   BEGIN
  1820.     GetDT(StartTime);
  1821.     FreeUpMemory;
  1822.     MyWin(MailScanWin,1,2,80,25,2,'Doing Mailscan',False);
  1823.     WITH MailScanWin^ DO
  1824.     BEGIN
  1825.       wFastText('Action      :', 2, 2);
  1826.       wFastText('Current file:', 4, 2);
  1827.       wFastText('Current area:', 6, 2);
  1828.       wFastText('# Scanned   :', 8, 2);
  1829.       wFastText('# Sent      :',10, 2);
  1830.       wFastText('# Tossed    :',12, 2);
  1831.       wFastText('# Bad msgs. :',14, 2);
  1832.       wFastText('# Dupe msgs.:',16, 2);
  1833.       wFastText('Memory usage:',18, 2);
  1834.     END;
  1835.     OldTagName:='';
  1836.     PktBufCount:=0;
  1837.     FOR i:=1 TO MaxDupeBases DO
  1838.     BEGIN
  1839.       New(Dupe[i]);
  1840.       FillChar(Dupe[i]^,SizeOf(DupeMemType),0);
  1841.     END;
  1842.     DupeFile.Open(StartPath+PoPMsgDupeFileName,SizeOf(DupeBaseType),True);
  1843.     Message('Reading ECHO MAIL Areas, and scanning dupe database',2);
  1844.     ReadAreasBBS(AreasBBS);
  1845.     SetUpMessageBase;
  1846.     New(Msg);
  1847.     New(SeenByTab);
  1848.     New(PathTab);
  1849.     IF Cfg.MailScanner.SaveDupesDir<>'' THEN DupeMsgNum:=GetHighestMsg(Cfg.MailScanner.SaveDupesDir);
  1850.     MatrixNum:=GetHighestMsg(Cfg.MailScanner.NetMailDir);
  1851.     BadMsgNum:=GetHighestMsg(Cfg.MailScanner.BadMsgs);
  1852. {$IFDEF DPMI}
  1853.     IF MaxAvail>1024*1024 THEN MaxPktBuffer:=61440 ELSE
  1854. {$ENDIF}
  1855.       IF MaxAvail>204800 THEN MaxPktBuffer:=32768 ELSE
  1856.         IF MaxAvail>153600 THEN MaxPktBuffer:=20480 ELSE
  1857.           MaxPktBuffer:=10240;
  1858.     UsedMemStatus;
  1859.     DoMailScan;
  1860.     FOR i:=1 TO MaxDupeBases DO
  1861.       Dispose(Dupe[i]);
  1862.     Dispose(PathTab);
  1863.     Dispose(SeenByTab);
  1864.     Dispose(Msg);
  1865.     FinishMessageBase;
  1866.     KillWindow(MailScanWin);
  1867.     InitialiseNodeList(Cfg.NodeList,Cfg.NodeListTyp);
  1868.     GetDT(EndTime);
  1869.     UpdateUsageStat(StartTime,EndTime,USMailProcess);
  1870.   END;
  1871. END;
  1872.  
  1873. END.
  1874.